package com.gitee.apanlh.util.algorithm;

import com.gitee.apanlh.exp.algorithm.InitKeyPairException;
import com.gitee.apanlh.exp.algorithm.InitPrivateKeyException;
import com.gitee.apanlh.util.algorithm.digest.MD5;
import com.gitee.apanlh.util.base.Choose;
import com.gitee.apanlh.util.base.ChooseOr;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.encode.Base64Type;
import com.gitee.apanlh.util.encode.Base64Utils;
import com.gitee.apanlh.util.encode.HexUtils;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.log.Log;
import com.gitee.apanlh.util.random.RandomUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.sys.test.TestUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.RegexUtils;
import com.gitee.apanlh.util.valid.ValidParam;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

/**
 * 	生成密钥工具类(对称/非对称)
 * 	<br>按照强随机数生成器生成
 *  
 * 	@author Pan
 */
public class KeyUtils {
	
	/**
	 * 	默认构造函数
	 * 	
	 * 	@author Pan
	 */
	private KeyUtils() {
		// 不允许外部构造
		super();
	}
	
	/**
	 * 	用于编码key
	 * 	<br>默认十六进制形式
	 * 	
	 * 	@author Pan
	 * 	@param 	key		key
	 * 	@return	String
	 */
	public static String encode(byte[] key) {
		return encode(key, false);
	}
	
	/**
	 * 	用于编码key(true时则使用Base64, false则使用Hex)
	 * 	
	 * 	@author Pan
	 * 	@param 	key			key
	 * 	@param 	isBase64	true填充Base64 false填充Hex
	 * 	@return	String
	 */
	public static String encode(byte[] key, boolean isBase64) {
		Assert.isNotEmpty(key);
		return isBase64 ? Base64Utils.encodeToStr(key) : HexUtils.encode(key);
	}
	
	/**	
	 * 	用于解码key的字符串
	 * 	<br>仅限十六进制或base64
	 *
	 * 	@author Pan
	 * 	@param 	key		key
	 * 	@return	String
	 */
	public static byte[] decode(String key) {
		Assert.isNotEmpty(key);
		boolean hex = RegexUtils.isHex(key);
		if (hex) {
			return HexUtils.decode(key);
		}

		if (key.contains("_") || key.contains("-")) {
			return Base64Utils.decode(key, Base64Type.RFC4648_URLSAFE);
		}
		return Base64Utils.decode(key);
	}

	/**
	 * 	随机生成自定位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@param 	length	长度
	 * 	@return	byte[]
	 */
	public static byte[] generate(int length) {
		return RandomUtils.randomBytes(length, true);
	}

	public static void main(String[] args) {
		TestUtils.taskTime(1, 1, () -> {
			for (long i = 10000000000l; i < 10999999999l; i++) {
				String s = MD5.create().digestToHex(ClassConvertUtils.toStr(i));
			}
		});
	}

	/**
	 * 	随机生成自定位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	length	长度
	 * 	@return	String
	 */
	public static String generateToHexStr(int length) {
		return HexUtils.encode(RandomUtils.randomBytes(length, true));
	}
	
	/**
	 * 	随机生成自定位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@param 	length	长度
	 * 	@return	byte[]
	 */
	public static byte[] generateToBase64(int length) {
		return Base64Utils.encode(RandomUtils.randomBytes(length, true)); 
	}
	
	/**
	 * 	随机生成自定位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	length	长度
	 * 	@return	String
	 */
	public static String generateToBase64Str(int length) {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(length, true)); 
	}
	
	/**
	 * 	随机生成40位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate40() {
		return RandomUtils.randomBytes(5, true);
	}
	
	/**
	 * 	随机生成64位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate64() {
		return RandomUtils.randomBytes(8, true);
	}
	
	/**
	 * 	随机生成128位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate128() {
		return RandomUtils.randomBytes(16, true); 
	}
	
	/**
	 * 	随机生成192位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate192() {
		return RandomUtils.randomBytes(24, true); 
	}
	
	/**
	 * 	随机生成256位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate256() {
		return RandomUtils.randomBytes(32, true); 
	}
	
	/**
	 * 	随机生成384位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate384() {
		return RandomUtils.randomBytes(48, true); 
	}
	
	/**
	 * 	随机生成512位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate512() {
		return RandomUtils.randomBytes(64, true); 
	}
	
	/**
	 * 	随机生成1024位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate1024() {
		return RandomUtils.randomBytes(128, true); 
	}
	
	/**
	 * 	随机生成2048位长度Key
	 * 	<br>byte[]
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate2048() {
		return RandomUtils.randomBytes(256, true); 
	}

	/**
	 * 	随机生成40位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate40ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(5, true));
	}
	
	/**
	 * 	随机生成64位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate64ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(8, true)); 
	}
	
	/**
	 * 	随机生成128位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate128ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(16, true)); 
	}
	
	/**
	 * 	随机生成192位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate192ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(24, true)); 
	}
	
	/**
	 * 	随机生成256位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate256ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(32, true)); 
	}
	
	/**
	 * 	随机生成384位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate384ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(48, true)); 
	}
	
	/**
	 * 	随机生成512位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate512ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(64, true)); 
	}
	
	/**
	 * 	随机生成1024位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate1024ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(128, true)); 
	}
	
	/**
	 * 	随机生成2048位长度Key
	 * 	<br>十六进制字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate2048ToHexStr() {
		return HexUtils.encode(RandomUtils.randomBytes(256, true)); 
	}
	
	/**
	 * 	随机生成40位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate40ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(5, true));
	}
	
	/**
	 * 	随机生成64位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate64ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(8, true)); 
	}
	
	/**
	 * 	随机生成128位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate128ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(16, true)); 
	}
	
	/**
	 * 	随机生成192位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate192ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(24, true)); 
	}
	
	/**
	 * 	随机生成256位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate256ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(32, true)); 
	}
	
	/**
	 * 	随机生成384位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate384ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(48, true)); 
	}
	
	/**
	 * 	随机生成512位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate512ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(64, true)); 
	}
	
	/**
	 * 	随机生成1024位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate1024ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(128, true)); 
	}
	
	/**
	 * 	随机生成2048位长度Key
	 * 	<br>Base64
	 * 	
	 * 	@author Pan
	 * 	@return	byte[]
	 */
	public static byte[] generate2048ToBase64() {
		return Base64Utils.encode(RandomUtils.randomBytes(256, true)); 
	}
	
	/**
	 * 	随机生成40位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate40ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(5, true));
	}
	
	/**
	 * 	随机生成64位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate64ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(8, true)); 
	}
	
	/**
	 * 	随机生成128位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate128ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(16, true)); 
	}
	
	/**
	 * 	随机生成192位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate192ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(24, true)); 
	}
	
	/**
	 * 	随机生成256位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate256ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(32, true)); 
	}
	
	/**
	 * 	随机生成384位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate384ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(48, true)); 
	}
	
	/**
	 * 	随机生成512位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate512ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(64, true)); 
	}
	
	/**
	 * 	随机生成1024位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate1024ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(128, true)); 
	}
	
	/**
	 * 	随机生成2048位长度Key
	 * 	<br>Base64字符串
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String generate2048ToBase64Str() {
		return Base64Utils.encodeToStr(RandomUtils.randomBytes(256, true)); 
	}
	
	/**	
	 * 	获取Key长度
	 * 	
	 * 	@author Pan
	 * 	@param 	str	 字符串
	 * 	@return	int  位数
	 */
	public static int getKeyLength(String str) {
		if (ValidParam.isEmpty(str)) {
			return 0;
		}
		if (RegexUtils.isHex(str)) {
			return HexUtils.decode(str).length * 8;
		}
		try {
			byte[] decode = Base64Utils.decode(str);
			return decode.length * 8;
		} catch (Exception e) {
			return StrEncodeUtils.utf8EncodeToBytes(str).length * 8;
		}
	}
	
	/**	
	 * 	获取Key长度
	 * 	<br>仅限于对称加密
	 * 	
	 * 	@author Pan
	 * 	@param 	bytes	 字节数组
	 * 	@return	int  	位数
	 */
	public static int getKeyLength(byte[] bytes) {
		if (bytes == null || bytes.length == 0) {
			return 0;
		}
		try {
			return Base64Utils.decode(bytes).length * 8;
		} catch (Exception e) {
			return bytes.length * 8;
		}
	}
	
	/**	
	 * 	生成公钥
	 * 	<br>自定义算法及Provider
	 * 	
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 *  @param 	provider	Provider对象，用于提供指定算法的实现
	 * 	@param 	publicKey	公钥的KeySpec对象，用于定义公钥的特征值和算法参数
	 * 	@return	KeyPair
	 */
	public static PublicKey generatePublicKey(String algorithm, Provider provider, KeySpec publicKey) {
		KeyFactory instance = ChooseOr.create(provider == null, () -> KeyFactory.getInstance(algorithm))
				.orElse(() -> KeyFactory.getInstance(algorithm, provider));
		try {
			return instance.generatePublic(publicKey);
		} catch (Exception e) {
			throw new InitPrivateKeyException(e.getMessage(), e);
		}
	}

	/**
	 * 	生成私钥
	 * 	<br>自定义算法及Provider
	 * 	
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 *  @param 	provider	Provider对象，用于提供指定算法的实现
	 * 	@param 	privateKey	私钥的KeySpec对象，用于定义私钥的特征值和算法参数
	 * 	@return	KeyPair
	 */
	public static PrivateKey generatePrivateKey(String algorithm, Provider provider, KeySpec privateKey) {
		KeyFactory instance = ChooseOr.create(provider == null, () -> KeyFactory.getInstance(algorithm))
				.orElse(() -> KeyFactory.getInstance(algorithm, provider));
		try {
			return instance.generatePrivate(privateKey);
		} catch (Exception e) {
			throw new InitPrivateKeyException(e.getMessage(), e);
		}
	}
	
	/**	
	 * 	生成密钥对
	 * 	<br>默认Provider
	 * 	<br>默认长度512位
	 * 	
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 * 	@return	KeyPair
	 */
	public static KeyPair generateKeyPair(String algorithm) {
		return generateKeyPair(algorithm, null, -1);
	}
	
	/**	
	 * 	生成密钥对
	 * 	<br>使用SunRsaSign作为Provider
	 * 	<br>自定义密钥长度
	 * 
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 * 	@param 	keyLength	密钥长度
	 * 	@return	KeyPair
	 */
	public static KeyPair generateKeyPair(String algorithm, int keyLength) {
		return generateKeyPair(algorithm, null, keyLength);
	}
	
	/**	
	 * 	生成密钥对
	 * 	<br>自定义Provider以及密钥长度
	 * 	<br>如果provider默认传递null将使用SunRsaSign作为provider使用
	 * 	
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 * 	@param 	provider	provider
	 * 	@param 	keyLength	密钥长度
	 * 	@return	KeyPair
	 */
	public static KeyPair generateKeyPair(String algorithm, Provider provider, int keyLength) {
		return generateKeyPair(algorithm, provider, keyLength, null);
	}
	
	/**	
	 * 	生成密钥对
	 * 	<br>自定义Provider以及密钥长度
	 * 	<br>如果provider默认传递null将使用SunRsaSign作为provider使用
	 * 	
	 * 	@author Pan
	 * 	@param 	algorithm	算法
	 * 	@param 	provider	provider
	 * 	@param 	keyLength	密钥长度
	 * 	@param 	params		算法参数
	 * 	@return	KeyPair
	 */
	public static KeyPair generateKeyPair(String algorithm, Provider provider, int keyLength, AlgorithmParameterSpec params) {
		KeyPairGenerator instance = ChooseOr.create(provider == null, () -> KeyPairGenerator.getInstance(algorithm))
				.orElse(() -> KeyPairGenerator.getInstance(algorithm, provider));
		
		try {
			if (algorithm.startsWith("RSA") && Choose.leftOpen(512, keyLength)) {
				Log.get().debug("key:[{}], minimum number of bit is not enough will be changed to 512 bit", keyLength);
				keyLength = 512;
			}
			instance.initialize(keyLength);
			if (ValidParam.isNotNull(params)) {
				instance.initialize(params);
			}
			return instance.generateKeyPair();
		} catch (Exception e) {
			throw new InitKeyPairException(StringUtils.format("algorithm[{}] length[{}] error[{}]", algorithm, keyLength, e.getMessage()), e);
		}
	}
}
